#pragma once

#include "Matrix.hpp"
namespace zzz{
template <typename T>
class Matrix<4, 4, T> : public MatrixBase<4, 4, T>
{
public:
  Matrix(){}
  Matrix(const Matrix<4, 4, T> &mat):MatrixBase<4, 4, T>(mat){}
  Matrix(const MatrixBase<4, 4, T> &mat):MatrixBase<4, 4, T>(mat){}
  template<typename T1> explicit Matrix<4, 4, T>(const MatrixBase<4, 4,T1>& a):MatrixBase<4, 4, T>(a) {}
  template<typename T1> explicit Matrix<4, 4, T>(const Matrix<4, 4,T1>& a):MatrixBase<4, 4, T>(a) {}
  template<typename T1> explicit Matrix<4, 4, T>(const T1 *p):MatrixBase<4, 4, T>(p) {}
  Matrix(const T& a,const T& b,const T& c,const T& d,const T& e,const T& f,const T& g,const T& h,const T& i,const T& j,const T& k,const T& l,const T& m,const T& n,const T& o,const T& p)
  {
    T* v=Data();
    v[0]=a; v[1]=b; v[2]=c; v[3]=d;
    v[4]=e; v[5]=f; v[6]=g; v[7]=h;
    v[8]=i; v[9]=j; v[10]=k; v[11]=l;
    v[12]=m; v[13]=n; v[14]=o; v[15]=p;
  }
  explicit Matrix(const T *p)
  {
    T* v=Data();
    v[0]=p[0];  v[1]=p[1];  v[2]=p[2];  v[3]=p[3];  
    v[4]=p[4];  v[5]=p[5];  v[6]=p[6];  v[7]=p[7];
    v[8]=p[8];  v[9]=p[9];  v[10]=p[10];  v[11]=p[11];
    v[12]=p[12];  v[13]=p[13];  v[14]=p[14];  v[15]=p[15];
  }
  Matrix(const Vector<3, T>& a,const Vector<3, T>& b,const Vector<3, T>& c)
  {
    T* v=Data();
    v[0]=a[0]; v[1]=a[1]; v[2]=a[2]; v[3]=0;
    v[4]=b[0]; v[5]=b[1]; v[6]=b[2]; v[7]=0;
    v[8]=c[0]; v[9]=c[1]; v[10]=c[2]; v[11]=0;
    v[12]=0; v[13]=0; v[14]=0; v[15]=1;
  }

  Matrix(const Vector<4, T>& a,const Vector<4, T>& b,const Vector<4, T>& c, const Vector<4, T> &d)
  {
    T* v=Data();
    v[0]=a[0]; v[1]=a[1]; v[2]=a[2]; v[3]=a[3];
    v[4]=b[0]; v[5]=b[1]; v[6]=b[2]; v[7]=b[3];
    v[8]=c[0]; v[9]=c[1]; v[10]=c[2]; v[11]=c[3];
    v[12]=d[0]; v[13]=d[1]; v[14]=d[2]; v[15]=d[3];
  }
  explicit Matrix(const Vector<4, T> *Data())
  {
    T* v=Data();
    v[0]=v[0][0]; v[1]=v[0][1]; v[2]=v[0][2]; v[3]=v[0][3];
    v[4]=v[1][0]; v[5]=v[1][1]; v[6]=v[1][2]; v[7]=v[1][3];
    v[8]=v[2][0]; v[9]=v[2][1]; v[10]=v[2][2]; v[11]=v[2][3];
    v[12]=v[3][0]; v[13]=v[3][1]; v[14]=v[3][2]; v[15]=v[3][3];
  }

  //assign
  inline const Matrix<4, 4, T> &operator=(const Matrix<4, 4, T>& a){MatrixBase<4, 4, T>::operator =(a); return *this;}
  using MatrixBase<4, 4, T>::operator=;
  using MatrixBase<4, 4, T>::Data;

  //alias
  const T& m00() const {return Data()[0];}
  const T& m01() const {return Data()[1];}
  const T& m02() const {return Data()[2];}
  const T& m03() const {return Data()[3];}
  const T& m10() const {return Data()[4];}
  const T& m11() const {return Data()[5];}
  const T& m12() const {return Data()[6];}
  const T& m13() const {return Data()[7];}
  const T& m20() const {return Data()[8];}
  const T& m21() const {return Data()[9];}
  const T& m22() const {return Data()[10];}
  const T& m23() const {return Data()[11];}
  const T& m30() const {return Data()[12];}
  const T& m31() const {return Data()[13];}
  const T& m32() const {return Data()[14];}
  const T& m33() const {return Data()[15];}
  const T& v0() const {return Data()[0];}
  const T& v1() const {return Data()[1];}
  const T& v2() const {return Data()[2];}
  const T& v3() const {return Data()[3];}
  const T& v4() const {return Data()[4];}
  const T& v5() const {return Data()[5];}
  const T& v6() const {return Data()[6];}
  const T& v7() const {return Data()[7];}
  const T& v8() const {return Data()[8];}
  const T& v9() const {return Data()[9];}
  const T& v10() const {return Data()[10];}
  const T& v11() const {return Data()[11];}
  const T& v12() const {return Data()[12];}
  const T& v13() const {return Data()[13];}
  const T& v14() const {return Data()[14];}
  const T& v15() const {return Data()[15];}
  T& m00() {return Data()[0];}
  T& m01() {return Data()[1];}
  T& m02() {return Data()[2];}
  T& m03() {return Data()[3];}
  T& m10() {return Data()[4];}
  T& m11() {return Data()[5];}
  T& m12() {return Data()[6];}
  T& m13() {return Data()[7];}
  T& m20() {return Data()[8];}
  T& m21() {return Data()[9];}
  T& m22() {return Data()[10];}
  T& m23() {return Data()[11];}
  T& m30() {return Data()[12];}
  T& m31() {return Data()[13];}
  T& m32() {return Data()[14];}
  T& m33() {return Data()[15];}
  T& v0() {return Data()[0];}
  T& v1() {return Data()[1];}
  T& v2() {return Data()[2];}
  T& v3() {return Data()[3];}
  T& v4() {return Data()[4];}
  T& v5() {return Data()[5];}
  T& v6() {return Data()[6];}
  T& v7() {return Data()[7];}
  T& v8() {return Data()[8];}
  T& v9() {return Data()[9];}
  T& v10() {return Data()[10];}
  T& v11() {return Data()[11];}
  T& v12() {return Data()[12];}
  T& v13() {return Data()[13];}
  T& v14() {return Data()[14];}
  T& v15() {return Data()[15];}

};
typedef Matrix<4, 4,zint8> Matrix4x4i8;
typedef Matrix<4, 4,zuint8> Matrix4x4ui8;
typedef Matrix<4, 4,zint16> Matrix4x4i16;
typedef Matrix<4, 4,zuint16> Matrix4x4ui16;
typedef Matrix<4, 4,zint32> Matrix4x4i32;
typedef Matrix<4, 4,zuint32> Matrix4x4ui32;
typedef Matrix<4, 4,zint64> Matrix4x4i64;
typedef Matrix<4, 4,zuint64> Matrix4x4ui64;
typedef Matrix<4, 4,zfloat32> Matrix4x4f32;
typedef Matrix<4, 4,zfloat64> Matrix4x4f64;


typedef Matrix<4, 4,zuint> Matrix4x4ui;
typedef Matrix<4, 4,int> Matrix4x4i;
typedef Matrix<4, 4,float> Matrix4x4f;
typedef Matrix<4, 4,double> Matrix4x4d;
}
